package libs.game.sokoban;

import libs.game.Coordinates;

/**
 *              This class represents a path where the box can be pushod on.
 *
 * @author      Jan Papousek
 * @version     2009-03-12
 */
public class BoxPath extends AbstractPath {

    private Path personPath;

    /**
     *          It create box path.
     *
     * @param   gamePlan    Game plan
     * @param   box         Box coordinates.
     * @throws  IllegalArgumentException if the field does not contain a box.
     */
    public BoxPath(SokobanGamePlan gamePlan, Coordinates box) {
        if (gamePlan == null) {
            throw new NullPointerException("Argument 'gamePlan' is null.");
        }
        if (gamePlan == null) {
            throw new NullPointerException("Argument 'box' is null.");
        }
        if ((gamePlan.getField(box) != SokobanGamePlan.FIELD_BOX_ON_EMPTY) && (gamePlan.getField(box) != SokobanGamePlan.FIELD_BOX_ON_GOAL)) {
            throw new IllegalArgumentException("The field with the coordinates does not contain a box.");
        }
        path = new Integer[gamePlan.rows()][gamePlan.columns()];
        personPath = new PersonPath(gamePlan);
        compute(gamePlan, box, 0,box);
    }

    private void compute(SokobanGamePlan gamePlan, Coordinates field, int value, Coordinates box) {
        if (path[field.getY()][field.getX()] != null) {
            return;
        }
        if (!field.isInRange(0, 0, gamePlan.columns()-1, gamePlan.rows()-1)) {
            return;
        }
        switch (gamePlan.getField(field)) {
            default:
                return;
            case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
            case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                if (value != 0) {
                    return;
                }
            case SokobanGamePlan.FIELD_PERSON_ON_EMPTY:
            case SokobanGamePlan.FIELD_PERSON_ON_GOAL:
            case SokobanGamePlan.FIELD_EMPTY:
            case SokobanGamePlan.FIELD_GOAL:
                numOfPathFields++;
                path[field.getY()][field.getX()] = value;
                Coordinates north = new Coordinates(field.getX(),field.getY()-1);
                Coordinates south = new Coordinates(field.getX(),field.getY()+1);
                Coordinates west = new Coordinates(field.getX()-1, field.getY());
                Coordinates east = new Coordinates(field.getX()+1, field.getY());
                if (north.isInRange(0, 0, gamePlan.columns()-1, gamePlan.rows()-1) &&
                    south.isInRange(0, 0, gamePlan.columns()-1, gamePlan.rows()-1))
                {
                    if ((personPath.get(north)) != null || north.equals(box)) {
                        switch (gamePlan.getField(south)) {
                            default:
                                compute(gamePlan,south,value+1,box);
                                break;
                            case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
                            case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                            case SokobanGamePlan.FIELD_WALL:
                                break;
                        }
                    }
                    if ((personPath.get(south)) != null || south.equals(box)) {
                        switch (gamePlan.getField(north)) {
                            default:
                                compute(gamePlan,north,value+1,box);
                                break;
                            case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
                            case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                            case SokobanGamePlan.FIELD_WALL:
                                break;
                        }
                    }
                }
                if (west.isInRange(0, 0, gamePlan.columns()-1, gamePlan.rows()-1) &&
                    east.isInRange(0, 0, gamePlan.columns()-1, gamePlan.rows()-1))
                {
                    if ((personPath.get(west)) != null || west.equals(box)) {
                        switch (gamePlan.getField(east)) {
                            default:
                                compute(gamePlan,east,value+1,box);
                                break;
                            case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
                            case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                            case SokobanGamePlan.FIELD_WALL:
                                break;
                        }
                    }
                    if ((personPath.get(east)) != null || east.equals(box)) {
                        switch (gamePlan.getField(west)) {
                            default:
                                compute(gamePlan,west,value+1,box);
                                break;
                            case SokobanGamePlan.FIELD_BOX_ON_EMPTY:
                            case SokobanGamePlan.FIELD_BOX_ON_GOAL:
                            case SokobanGamePlan.FIELD_WALL:
                                break;
                        }
                    }
                }
                break;
        }
    }

}
